<template>
  <div>
    <!-- 主要表格区 -->
    <div class="main_table_area">
      <!-- 按钮 -->
      <div class="op_btns">
        <el-button type="danger" @click="batchDeleteByIds" class="el-icon-delete" plain size="small">批量删除选中接口</el-button>
        <el-button type="info" @click="batchDisableByIds" class="el-icon-turn-off" plain size="small">批量禁用选中接口</el-button>
        <el-button type="primary" @click="batchEnableByIds" class="el-icon-open" plain size="small">批量启用选中接口</el-button>
        <el-tooltip content="解析Controller类中的接口存入数据库,以供API授权使用">
          <el-button type="primary" @click="showFileParseDrawer" class="el-icon-set-up" plain
            size="small">API解析入库</el-button>
        </el-tooltip>
      </div>
      <!-- 搜索区 -->
      <div class="search_area">
        <!-- 下拉选项一 -->
        <div class="search_item">
          <el-select v-model="mainTable.searchData.apiGroup" placeholder="接口所属分组" @change="toSearch" size="small">
            <el-option label="所有分组" :value="null"></el-option>
            <el-option :label="g" :value="g" v-for="g in select.apiGroups" :key="g"></el-option>
          </el-select>
          <el-button icon="el-icon-refresh" size="small" @click="refreshApiGroups"></el-button>
        </div>
        <!-- 下拉选项二 -->
        <div class="search_item">
          <el-select v-model="mainTable.searchData.className" placeholder="接口所属类" @change="toSearch" size="small">
            <el-option label="所有类" :value="null"></el-option>
            <el-option :label="c" :value="c" v-for="c in select.apiClassNames" :key="c"></el-option>
          </el-select>
          <el-button icon="el-icon-refresh" size="small" @click="refreshApiClassNames"></el-button>
        </div>
        <!-- 下拉选项三 -->
        <div class="search_item">
          <el-select v-model="mainTable.searchData.requestMethod" placeholder="请求方式" @change="toSearch" size="small">
            <el-option label="所有方式" :value="null"></el-option>
            <el-option :label="m" :value="m" v-for="m in select.requestMethods" :key="m"></el-option>
          </el-select>
          <el-button icon="el-icon-refresh" size="small" @click="refreshApiRequestMethods"></el-button>
        </div>
        <!-- 下拉选项四 -->
        <div class="search_item">
          <el-select v-model="mainTable.searchData.enabled" placeholder="接口状态" @change="toSearch" size="small">
            <el-option label="所有状态" :value="null"></el-option>
            <el-option :label="s.label" :value="s.value" v-for="s in select.statusesWithLabel" :key="s.value"></el-option>
          </el-select>
          <el-button icon="el-icon-refresh" size="small" @click="refreshApiStatuses"></el-button>
        </div>
        <!-- 文本输入 -->
        <div class="search_item">
          <el-input v-model="mainTable.searchData.requestPath" placeholder="请求路径" clearable size="small"></el-input>
          <el-input v-model="mainTable.searchData.description" placeholder="描述" clearable size="small"></el-input>
          <el-button type="primary" @click="toSearch" class="el-icon-refresh" plain size="small">刷新表格</el-button>
        </div>
      </div>
      <!-- 表格 -->
      <el-table :data="mainTable.data" border highlight-current-row size="small"
        @selection-change="selectionChangeHandle">
        <template slot="empty">
          <el-empty description="没有任何接口数据"></el-empty>
        </template>
        <!-- 复选列 -->
        <el-table-column type="selection" fixed></el-table-column>
        <!-- 序号列 -->
        <el-table-column type="index" label="序号"></el-table-column>
        <!-- 展开列 -->
        <el-table-column type="expand" label="详情">
          <template slot-scope="{row}">
            <el-descriptions :title="row.description" :column="1" border size="small">
              <el-descriptions-item :label="col.label" v-for="col in mainTable.expandColumns" :key="col.id">
                <span>{{ row[col.prop] }}</span>
              </el-descriptions-item>
            </el-descriptions>
          </template>
        </el-table-column>
        <!-- 动态列 -->
        <el-table-column show-overflow-tooltip :min-width="col.minWidth" :label="col.label" :prop="col.prop"
          v-for="(col, index) in mainTable.columns" :key="index">
        </el-table-column>
        <!-- 特殊处理列 -->
        <el-table-column label="状态" width="70px">
          <template slot-scope="{row}">
            <el-tag type="success" v-if="row.enabled === 1">启用</el-tag>
            <el-tag type="info" v-else>禁用</el-tag>
          </template>
        </el-table-column>
        <!-- 操作列 -->
        <el-table-column label="操作" width="70px">
          <template slot-scope="{row}">
            <el-button type="primary" size="mini" @click="showEditItemDrawer(row)" class="el-icon-edit"></el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 主表分页区 -->
      <div style="padding:10px; display: flex; justify-content:space-around; ">
        <el-pagination hide-on-single-page layout="total, sizes, prev, pager, next, jumper" next-text="下一页"
          prev-text="上一页" @size-change="handleSizeChange" @current-change="handleCurrentChange"
          :current-page="mainTable.page.currentPage" :page-size="mainTable.page.pageSize" :total="mainTable.page.total">
        </el-pagination>
      </div>
    </div>

    <!-- Controller接口解析抽屉 -->
    <div>
      <el-drawer :visible="fileParseDawer.visible" :before-close="fileParseDrawerClose" size="100%" direction="rtl"
        title="Controller文件接口解析入库">
        <div class="controller_parse_area">
          <p>点击目录预览文件,点击文件解析其接口信息</p>
          <div>
            <!-- 面包屑：当前所在目录 -->
            <el-breadcrumb separator="\" v-show="fileParseDawer.resourceRootDir">
              <el-breadcrumb-item v-for="(path, index) in currentDirArr" :key="index">
                <button @click="breadcrumbClickHandle(index)" :disabled="disabledBreadcrumbItemByItsIndex(index)">
                  {{ path }}
                </button>
              </el-breadcrumb-item>
            </el-breadcrumb>

            <!-- 文件列表 -->
            <ul v-show="fileParseDawer.resourceRootDir">
              <!-- 固定项：返回上级 -->
              <li>
                <span title="返回上级目录" class="el-icon-folder-opened" @click="backToParentDirectory">../</span>
              </li>
              <!-- 动态项 -->
              <li v-for="f in fileParseDawer.data" :key="f.fullName">
                <span :class="bindItemClassByType(f.directoryOrNot)" @click="breadcrumbItemClickHandle(f)"
                  :title="f.absolutePath">
                  {{ f.fullName }}
                </span>
              </li>
            </ul>
          </div>
        </div>
      </el-drawer>
    </div>

    <!-- Controller接口编辑抽屉 -->
    <div>
      <el-drawer :visible="editItemDrawer.visible" size="50%" direction="ltr" :before-close="editItemDrawerClose"
        title="编辑接口">
        <div class="edit_item_area">
          <div id="edit_form">
            <el-form :model="editItemDrawer.data" ref="editItemFormRef" label-width="100px" label-position="left"
              size="small">
              <el-form-item label="请求路径" prop="requestPath">
                <el-input placeholder="请求路径" v-model="editItemDrawer.data.requestPath" clearable></el-input>
              </el-form-item>
              <el-form-item label="请求方式" prop="requestMethod">
                <el-radio-group v-model="editItemDrawer.data.requestMethod" size="small">
                  <el-radio-button :label="r" v-for="r in select.requestMethods" :key="r">{{ r }}
                  </el-radio-button>
                </el-radio-group>
              </el-form-item>
              <el-form-item label="接口描述" prop="description">
                <el-input type="textarea" placeholder="接口描述" v-model="editItemDrawer.data.description"
                  clearable></el-input>
              </el-form-item>
              <el-form-item label="接口所属分组" prop="apiGroup">
                <el-select v-model="editItemDrawer.data.apiGroup" placeholder="接口所属分组">
                  <el-option :label="g" :value="g" v-for="g in select.apiGroups" :key="g"></el-option>
                </el-select>
                <el-button @click="refreshApiGroups" class="el-icon-refresh">刷新</el-button>
              </el-form-item>
              <el-form-item label="接口所属类" prop="className">
                <el-select v-model="editItemDrawer.data.className" placeholder="接口所属类">
                  <el-option :label="g" :value="g" v-for="g in select.apiClassNames" :key="g"></el-option>
                </el-select>
                <el-button @click="refreshApiClassNames" class="el-icon-refresh">刷新</el-button>
              </el-form-item>
              <el-form-item label="接口状态" prop="enabled">
                <el-radio-group v-model="editItemDrawer.data.enabled" size="small">
                  <el-radio-button :label="s.value" v-for="s in select.statusesWithLabel" :key="s.value">{{ s.label }}
                  </el-radio-button>
                </el-radio-group>
              </el-form-item>
              <el-form-item>
                <el-button @click="editItemDrawer.visible = false">取消</el-button>
                <el-button type="primary" @click="editItem">提交</el-button>
              </el-form-item>
            </el-form>
          </div>
        </div>
      </el-drawer>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {

  data() {
    return {
      // 下拉选项
      select: {
        // 请求方式选项
        requestMethods: [],
        // 接口状态选项
        statusesWithLabel: [],
        // 接口所属类选项
        apiClassNames: [],
        // 接口所属分组选项
        apiGroups: [],
      },

      // 主要表格
      mainTable: {
        // 动态列
        columns: [
          {
            prop: 'stableId',
            label: '稳定ID',
            minWidth: '40px',
          },
          {
            prop: 'requestPath',
            label: '请求路径',
            minWidth: '100px',
          },
          {
            prop: 'description',
            label: '描述',
            minWidth: '40px',
          },
          {
            prop: 'requestMethod',
            label: '方式',
            minWidth: '30px',
          },
          {
            prop: 'apiGroup',
            label: '所属组',
            minWidth: '30px',
          },
        ],
        // 展开列
        expandColumns: [
          {
            prop: 'id',
            label: 'ID',
          },
          {
            prop: 'stableId',
            label: '稳定ID',
          },
          {
            prop: 'requestPath',
            label: '请求路径',
          },
          {
            prop: 'requestMethod',
            label: '方式',
          },
          {
            prop: 'apiGroup',
            label: '所属组',
          },
          {
            prop: 'className',
            label: '所属类',
          },
          {
            prop: 'createAt',
            label: '解析时间',
          },
          {
            prop: 'creatorName',
            label: '解析者',
          },
          {
            prop: 'updateAt',
            label: '更新时间',
          },
          {
            prop: 'updaterName',
            label: '更新者',
          },
        ],
        // 表格数据
        data: [],
        // 表格选择数据
        selectedData: [],
        // 分页数据
        page: {
          currentPage: 1,
          pageSize: 10,
          total: 0,
        },
        // 搜索数据
        searchData: {
          requestPath: '',
          requestMethod: null,
          apiGroup: null,
          description: '',
          enabled: null,
          className: null,
        },
      },
      // Controller接口解析抽屉
      fileParseDawer: {
        // 抽屉可见性
        visible: false,
        // 可访问的根路径
        resourceRootDir: '',
        // 文件信息数据
        data: [],
      },
      // Controller接口编辑抽屉
      editItemDrawer: {
        visible: false,
        // 绑定数据
        data: {
          id: 0,
          requestPath: "",
          requestMethod: "",
          description: "",
          apiGroup: "",
          className: "",
          enabled: 0,
        }
      },
    }
  },

  computed: {
    // 借助mapGetters生成计算属性：uiAuthValidateIsNotNeeded、apiAuths
    ...mapGetters('uiAuthModule', ['uiAuthValidateIsNotNeeded', 'apiAuths']),
    // 各个操作可用性计算属性

    /**
     * 计算属性：当前文件所在的目录分割成数组
     */
    currentDirArr() {
      let curAbsolutePathArr = []
      // 若文件列表不为空数组
      if (this.fileParseDawer.data && this.fileParseDawer.data.length > 0) {
        curAbsolutePathArr = this.fileParseDawer.data[0].absolutePath.split('\\')
        // 去掉文件名，得到文件所在目录
        curAbsolutePathArr.pop()
      } else {
        curAbsolutePathArr = this.fileParseDawer.resourceRootDir
      }
      return curAbsolutePathArr
    },

    /**
     * 计算属性：可访问资源根目录分割为数组
     */
    resourceRootDirArr() {
      return this.fileParseDawer.resourceRootDir.split('\\')
    }
  },

  methods: {
    /**
     * 表格总数据量改变时调用此方法
     * @returns 总数据量改变后，当前页面尺寸下的最后一页
    */
    computeLastPageNum(page, changeCount) {
      const newTotal = page.total + changeCount
      page.total = newTotal
      return Math.ceil(newTotal / page.pageSize)
    },

    //#region 下拉选项方法开始
    // 刷新接口所属类
    async refreshApiClassNames() {
      const { data: res } = await this.$axios("/admin/interface/apiClassNames")
      if (res.code === 0) {
        this.$message.error(res.msg)
        return
      }
      this.select.apiClassNames = res.data
    },
    // 刷新接口分组
    async refreshApiGroups() {
      const { data: res } = await this.$axios("/admin/interface/apiGroups")
      if (res.code === 0) {
        this.$message.error(res.msg)
        return
      }
      this.select.apiGroups = res.data
    },
    // 刷新状态
    async refreshApiStatuses() {
      const { data: res } = await this.$axios("/admin/interface/apiStatuses")
      if (res.code === 0) {
        this.$message.error(res.msg)
        return
      }
      this.select.statusesWithLabel = res.data
    },
    // 刷新请求方式
    async refreshApiRequestMethods() {
      const { data: res } = await this.$axios("/admin/interface/apiRequestMethods")
      if (res.code === 0) {
        this.$message.error(res.msg)
        return
      }
      this.select.requestMethods = res.data
    },
    //#endregion 下拉选项方法结束

    //#region 主表方法开始
    // 表格复选
    selectionChangeHandle(selection) {
      this.mainTable.selectedData = selection
    },
    // 批量删除
    async batchDeleteByIds() {
      const ids = this.mainTable.selectedData.map(item => item.id)
      if (ids.length === 0) {
        this.$message.info("未选则要删除的接口")
      } else {
        try {
          await this.$confirm(`确定删除 ${ids.length} 条接口吗`)
          const { data: res } = await this.$axios({
            url: '/admin/interface/batchDeleteApis',
            method: 'delete',
            data: ids
          })
          if (res.code === 0) {
            this.$message.error(res.msg)
            return
          }
          let msg = ""
          res.data.forEach(delInfo => {
            msg += `<p>${delInfo.label}: ${delInfo.delCount}</P>`
          })
          this.$notify({
            title: '删除详细',
            duration: 0,
            type: 'success',
            dangerouslyUseHTMLString: true,
            message: msg
          });
          this.mainTable.page.currentPage = 1
          this.pagingGettableData()
        } catch (e) {
          if (e === 'cancel') { return }
          this.$message.error(`删除失败`)
          console.dir(e)
        }
      }
    },
    // 批量启用
    batchEnableByIds() {
      const ids = this.mainTable.selectedData.map(item => item.id)
      if (ids.length === 0) {
        this.$message.info("未选则要启用的接口")
      } else {
        this.$axios({
          url: '/admin/interface/batchEnableByIds',
          method: 'put',
          data: ids
        })
          .then((res) => {
            this.$message.success(`已启用 ${res.data.data} 个接口`)
            console.log(res)
            this.pagingGettableData()
          })
          .catch((err) => {
            this.$message.error(err.msg)
          })
      }
    },
    // 批量禁用
    batchDisableByIds() {
      const ids = this.mainTable.selectedData.map(item => item.id)
      if (ids.length === 0) {
        this.$message.info("未选则要禁用的接口")
      } else {
        this.$axios({
          url: '/admin/interface/batchDisableByIds',
          method: 'put',
          data: ids
        })
          .then((res) => {
            this.$message.success(`已禁用 ${res.data.data} 个接口`)
            this.pagingGettableData()
          })
          .catch((err) => {
            this.$message.error(err.msg)
          })
      }
    },
    // 清除搜索条件
    clearSearchCondition() {
      this.mainTable.searchData.description = null
      this.mainTable.searchData.requestPath = null
    },
    // 开始搜索
    toSearch() {
      this.clearSearchCondition()
      this.mainTable.page.currentPage = 1
      this.pagingGettableData()
    },
    // 分页获取接口数据
    async pagingGettableData() {
      try {
        const { data: res } = await this.$axios({
          url: `/admin/interface/pagingGetInterfaces/${this.mainTable.page.currentPage}/${this.mainTable.page.pageSize}`,
          method: 'post',
          data: this.mainTable.searchData
        })
        if (res.code === 0) {
          this.$message.error(res.msg)
          return
        }
        this.mainTable.data = res.data.data
        this.mainTable.page.total = res.data.total
      } catch (error) {
        this.$message.error(`获取数据失败`)
        console.dir(error)
      }
    },
    handleSizeChange(pageSize) {
      this.mainTable.page.pageSize = pageSize
      this.pagingGettableData()
    },
    handleCurrentChange(currentPage) {
      this.mainTable.page.currentPage = currentPage
      this.pagingGettableData()
    },
    //#endregion 主表方法结束

    //#region Controller接口解析方法开始
    // 隐藏文件解析抽屉
    fileParseDrawerClose(done) {
      this.fileParseDawer.visible = false
      done()
    },
    // 显示文件解析抽屉
    showFileParseDrawer() {
      this.fileParseDawer.visible = true
    },
    // 面包屑动态数据部分点击处理
    breadcrumbClickHandle(index) {
      const currDirArr = this.currentDirArr
      // 若重复点击当前目录，提示并返回
      if (index === currDirArr.length - 1) {
        this.$message.warning("已处于该目录")
        return
      }
      // 若访问的范围超过跟路径，提示并返回
      if (index < this.resourceRootDirArr.length - 1) {
        this.$message.error("无法访问")
        return
      }
      // 计算出栈次数(当前目录的元素个数 - 面包屑索引 - 1)
      const popCount = currDirArr.length - index - 1
      for (let i = 0; i < popCount; i++) {
        // 去掉当前索引后面的
        currDirArr.pop()
      }
      // 目标目录数组
      const targetDirArr = currDirArr
      this.expandChildrenOfDirectory(targetDirArr.join('\\'))
    },
    // 初始化可访问的资源根目录
    async initVisibilityResourceRootDir() {
      const { data: response } = await this.$axios.get('/admin/interface/visibilityOfResourceRoot')
      this.fileParseDawer.resourceRootDir = response.data
      this.expandChildrenOfDirectory(this.fileParseDawer.resourceRootDir)
    },
    // 返回当前目录的父目录
    async backToParentDirectory() {
      let curDirArr = this.currentDirArr
      // 若再次访问的目录为根目录，直接返回
      if (this.fileParseDawer.resourceRootDir === curDirArr.join("\\")) {
        this.$message.warning('无法再继续访问上级目录了~')
        return
      }
      try {
        // 否则获取数据
        const { data: response } = await this.$axios({
          url: '/admin/interface/returnParentDirOfCurDir',
          params: {
            specificFile: this.fileParseDawer.data[0].absolutePath
          }
        })
        // 更新当前文件信息
        this.fileParseDawer.data = response.data
      } catch (error) {
        console.log(error)
      }
    },
    // 展开指定目录的直接子文件列表
    async expandChildrenOfDirectory(dirAbsolutePath) {
      try {
        const { data: response } = await this.$axios({
          url: '/admin/interface/childrenOfDir',
          params: {
            dirAbsolutePath: dirAbsolutePath
          }
        })
        // 更新文件列表
        this.fileParseDawer.data = response.data
      } catch (error) {
        console.log(error)
      }
    },
    // 解析controller文件中的接口信息并入库
    async parseFile(file) {
      try {
        await this.$confirm(`确定解析 ${file.fullName} 中的接口信息吗?`)
        const { data: response } = await this.$axios({
          url: '/admin/interface/parseControllerFile',
          method: 'post',
          params: {
            controllerFile: file.absolutePath,
          }
        })
        if (response.code === 0) {
          this.$message.error(response.msg)
          return
        }
        if (response.code === 1) {
          this.$message.success(`${response.msg} 解析出${response.data}个接口`)
          const lastPageNum = this.computeLastPageNum(this.mainTable.page, response.data)
          this.mainTable.page.currentPage = lastPageNum
          this.pagingGettableData()
        }

      } catch (e) {
        if (e === 'cancel') {
          this.$message.info('取消解析')
          return
        }

        console.dir(e)
      }
    },
    // 根据面包屑条目对应的索引，决定是否将其自身禁用
    disabledBreadcrumbItemByItsIndex(index) {
      return index < this.resourceRootDirArr.length - 1
    },
    // 面包屑动态项点击分类处理
    breadcrumbItemClickHandle(file) {
      // 若为目录，将其展开
      if (file.directoryOrNot) {
        this.expandChildrenOfDirectory(file.absolutePath)
      }
      // 若为文件，解析
      else {
        this.parseFile(file)
      }
    },
    // 绑定动态项的类名，为目录和文件添加不同图标，以示区别
    bindItemClassByType(isDirectory) {
      if (isDirectory) {
        return 'el-icon-folder'
      }
      else {
        return 'el-icon-document'
      }
    },
    //#endregion Controller接口解析方法结束

    //#region Controller接口编辑方法开始
    // 显示抽屉
    showEditItemDrawer(api) {
      this.getEditDataOfUserById(api.id)
      this.editItemDrawer.visible = true
    },
    // 获取编辑项的信息
    async getEditDataOfUserById(apiId) {
      const { data: res } = await this.$axios.get(`/admin/interface/interface/${apiId}`)
      if (res.code === 0) {
        this.$message.error(res.msg)
        return
      }
      this.editItemDrawer.data = res.data
    },
    // 隐藏抽屉，并刷新数据
    editItemDrawerClose(done) {
      this.editItemDrawer.visible = false
      this.pagingGettableData()
      done()
    },
    // 编辑请求
    async editItem() {
      try {
        await this.$confirm(`确定修改吗`)
        const { data: res } = await this.$axios({
          url: '/admin/interface/updateInterface',
          method: 'put',
          data: this.editItemDrawer.data,
        })
        if (res.code === 0) {
          this.$message.error(res.msg)
          return
        } else if (res.data === 0) {
          this.$message.info(`修改失败`)
          return
        }
        this.$message.success(res.msg)
      } catch (e) {
        if (e === 'cancel') {
          this.$message.info(`取消修改`)
        }
      }
    },
    //#endregion Controller接口编辑方法结束
  },

  // 钩子
  mounted() {
    this.initVisibilityResourceRootDir()
    this.pagingGettableData()

    this.refreshApiClassNames()
    this.refreshApiGroups()
    this.refreshApiRequestMethods()
    this.refreshApiStatuses()
  }
}
</script>

<style lang="less" scoped>
.main_table_area {
  .op_btns {
    display: flex;
    justify-content: start;
    padding-bottom: 10px;
  }

  .search_area {
    display: flex;
    justify-content: start;
    padding-bottom: 10px;

    .search_item {
      display: flex;
      justify-content: center;
      align-items: center;
      padding-right: 20px;

      .el-select {
        max-width: 100px;
      }

      .el-input {
        max-width: 130px;
        padding-right: 5px;
      }
    }
  }
}


.controller_parse_area {
  width: 90%;
  margin: 0 auto;

  ul {
    list-style: none;
    padding: 0px;
    border-left: 1px solid #000;
    padding-left: 10px;
  }

  ul li {
    display: block;
    padding: 5px 0px 5px 0px;
  }

  ul li:nth-child(1) span {
    color: #409eff;
  }

  ul li span {
    padding: 2px;
    cursor: pointer;
  }
}

.edit_item_area {
  height: 100%;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: center;

  #edit_form {
    width: 450px;
    padding: 50px;
    border-radius: 10px;
    box-shadow: 0 0 3px #000;
  }
}
</style>